0x00

在 Google 的 Inbox app 中,有这样一个场景:点击左上角按钮,弹出侧边栏,空白部分形成一个黑色半透明的遮罩,同时 status bar 隐藏,如下图

view 层面为了实现对 navigation bar 的遮罩,使用 keyWindow 最直接。而要实现隐藏 status bar 的效果,如果直接使用UIApplication.shared.isStatusBarHidden = true语句,会产生这样的效果

随着 status bar 的隐藏,navigation bar 在 Y 轴的位置缩小了 20px,高度从 64px 变成了原本的 44px。


0x01

显然,单纯的一条隐藏 status bar 的语句不能满足需求,我在网上找了很多代码来试,都没法满足,除此之外就是一些 hack 做法了。后来看了一个开源库的源码,发现了一个不错的做法

1
2
3
4
5
6
7
// show menu & hide status bar
let keyWindow = UIApplication.shared.keyWindow
keyWindow?.windowLevel = UIWindowLevelStatusBar + 1
keyWindow?.addSubview(menuView)
// hide menu & show status bar
UIApplication.shared.keyWindow?.windowLevel = UIWindowLevelNormal

这段代码把当前的 keyWindow 的 windowLevel属性设置的大于 status bar 的 windowLevel,这样 status bar 实际上没有隐藏,只是它所在的 UIWindow 被当前的 keyWindow 覆盖了。

效果如下

这样看上去就和 Inbox 有几分相似了


0x02

为什么改变了 keyWindow 的 windowLevel,会导致 status bar 被隐藏,而不是被遮挡呢?
实际上 status bar 有自己的 UIWindow,它的 windowLevel 等于 UIWindowLevelStatusBar,高于我们平常的 UIWindowLevelNormal,所以会优先显示 status bar,我们改变了 keyWindow 的 windowLevel 之后,由于 navigation bar 不透明,所以我们看不到它背后的 status bar,达到了隐藏 status bar 的效果。而 navigation bar 的高度不变,只要 status bar 不是隐藏状态,navigation bar 的高度就会在原始高度上加 20px。

本来我想输出一下 UIApplication.shared.windows 看看是否真的有 status bar 的 UIWindow,但是输出结果并没有。官方文档中对这个属性的解释如下:

This property contains the UIWindow objects currently associated with the app. This list does not include windows created and managed by the system, such as the window used to display the status bar.
The windows in the array are ordered from back to front by window level; thus, the last window in the array is on top of all other app windows.

这也印证了 status bar 确实有自己的 UIWindow,只是无法通过这个属性得到它。

现在就可以根据这个方法隐藏 status bar 而不改变 navigation bar 的高度了 :-p